using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Diagnostics;
using VistaBatterySaver.Properties;

namespace VistaBatterySaver
{
    internal class PowerMngr
    {
        [DllImport(@"User32", SetLastError = true, EntryPoint = "RegisterPowerSettingNotification", CallingConvention = CallingConvention.StdCall)]
        private static extern IntPtr RegisterPowerSettingNotification(
            IntPtr hRecipient,
            ref Guid PowerSettingGuid,
            Int32 Flags);

        [DllImport(@"User32", EntryPoint = "UnregisterPowerSettingNotification", CallingConvention = CallingConvention.StdCall)]
        private static extern bool UnregisterPowerSettingNotification(IntPtr handle);


        internal const int WM_POWERBROADCAST = 0x0218;

        /// <summary>
        /// Notifies each time the remaining battery capacity changes. The granularity varies from system to system but the finest granularity is 1 percent. The Data member is a DWORD that indicates the current battery capacity remaining, in percentage from 0 through 100. The Data member has no information and can be ignored. 
        /// </summary>
        static Guid GUID_BATTERY_PERCENTAGE_REMAINING = new Guid("A7AD8041-B45A-4CAE-87A3-EECBB468A9E1");
        
        /// <summary>
        /// The monitor on/off notification indicates when the primary system monitor is on or off. This notification is useful for components that actively render content to the display device, such as media visualization. These applications should register for this notification and stop rendering graphics content when the monitor is off to reduce system power consumption. The Data member is a DWORD that indicates the current monitor state. 
        /// 0x0 
        /// The monitor is off. 
        /// 0x1 
        /// The monitor is on. 
        /// </summary>
        static Guid GUID_MONITOR_POWER_ON = new Guid(0x02731015, 0x4510, 0x4526, 0x99, 0xE6, 0xE5, 0xA1, 0x7E, 0xBD, 0x1A, 0xEA);

        /// <summary>
        /// Notifies each time the system power source changes. The Data member is a DWORD with values from the SYSTEM_POWER_CONDITION enumeration that indicates the current power source. 
        /// PoAc (zero) 
        /// The computer is powered by an AC power source (or similar, such as a laptop powered by a 12V automotive adapter). 
        /// PoDc (one) 
        /// The computer is powered by an onboard battery power source. 
        /// PoHot (two) 
        /// The computer is powered by a short-term power source such as a UPS device. 
        /// </summary>
        static Guid GUID_ACDC_POWER_SOURCE = new Guid(0x5D3E9A59, 0xE9D5, 0x4B00, 0xA6, 0xBD, 0xFF, 0x34, 0xFF, 0x51, 0x65, 0x48);
        /// <summary>
        /// The idle/background task notification is delivered when the system is busy. This indicates that the system will not be moving into an idle state in the near future and that the current time is a good time for components to perform background or idle tasks that would otherwise prevent the computer from entering an idle state. There is no notification when the system is able to move into an idle state. The background idle task notification does not indicate if a user is present or not the computer. The Data member has no information and can be ignored. 
        /// </summary>
        static Guid GUID_IDLE_BACKGROUND_TASK = new Guid(0x515c31d8, 0xf734, 0x163d, 0xa0, 0xfd, 0x11, 0xa0, 0x8c, 0x91, 0xe8, 0xf1);
        /// <summary>
        /// The away mode notification indicates that the system is entering or exiting away mode. The Data member is a DWORD that indicates the current away mode state. 
        /// 0x0 
        /// The computer is exiting away mode. 
        /// 0x1 
        /// The computer is entering away mode. 
        /// </summary>
        static Guid GUID_SYSTEM_AWAYMODE = new Guid(0x98a7f580, 0x01f7, 0x48aa, 0x9c, 0x0f, 0x44, 0x35, 0x2c, 0x29, 0xe5, 0xC0);
        /// <summary>
        /// Notifies each time the active power scheme personality changes. All power schemes map to one of these personalities. The Data member is a GUID that indicates the new active power scheme personality. 
        /// </summary>
        static Guid GUID_POWERSCHEME_PERSONALITY = new Guid(0x245D8541, 0x3943, 0x4422, 0xB0, 0x25, 0x13, 0xA7, 0x84, 0xF6, 0x79, 0xB7);
        
        static Guid GUID_MAX_POWER_SAVINGS = new Guid(0xA1841308, 0x3541, 0x4FAB, 0xBC, 0x81, 0xF7, 0x15, 0x56, 0xF2, 0x0B, 0x4A);
        // No Power Savings - Almost no power savings measures are used.
        static Guid GUID_MIN_POWER_SAVINGS = new Guid(0x8C5E7FDA, 0xE8BF, 0x4A96, 0x9A, 0x85, 0xA6, 0xE2, 0x3A, 0x8C, 0x63, 0x5C);
        // Typical Power Savings - Fairly aggressive power savings measures are used.
        static Guid GUID_TYPICAL_POWER_SAVINGS = new Guid(0x381B4222, 0xF694, 0x41F0, 0x96, 0x85, 0xFF, 0x5B, 0xB2, 0x60, 0xDF, 0x2E);

        internal static Guid GUID_WAKE_ON_TIMER = new Guid(0xbd3b718a, 0x0680, 0x4d9d, 0x8a, 0xb2, 0xe1, 0xd2, 0xb4, 0xac, 0x80, 0x6d);


        // Win32 decls and defs
        //
        const int PBT_APMQUERYSUSPEND = 0x0000;
        const int PBT_APMQUERYSTANDBY = 0x0001;
        const int PBT_APMQUERYSUSPENDFAILED = 0x0002;
        const int PBT_APMQUERYSTANDBYFAILED = 0x0003;
        const int PBT_APMSUSPEND = 0x0004;
        const int PBT_APMSTANDBY = 0x0005;
        const int PBT_APMRESUMECRITICAL = 0x0006;
        const int PBT_APMRESUMESUSPEND = 0x0007;
        const int PBT_APMRESUMESTANDBY = 0x0008;
        const int PBT_APMBATTERYLOW = 0x0009;
        const int PBT_APMPOWERSTATUSCHANGE = 0x000A; // power status
        const int PBT_APMOEMEVENT = 0x000B;
        const int PBT_APMRESUMEAUTOMATIC = 0x0012;
        const int PBT_POWERSETTINGCHANGE = 0x8013; // DPPE



        const int DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000;
        const int DEVICE_NOTIFY_SERVICE_HANDLE = 0x00000001;

        // This structure is sent when the PBT_POWERSETTINGSCHANGE message is sent.
        // It describes the power setting that has changed and contains data about the change
        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        internal struct POWERBROADCAST_SETTING
        {
            public Guid PowerSetting;
            public Int32 DataLength;
        }


        IntPtr hPowerSrc, hBattCapacity, hMonitorOn, hPowerScheme;

        internal void RegisterForPowerNotifications(IntPtr hwnd)
        {
            hPowerSrc = RegisterPowerSettingNotification(hwnd,
            ref GUID_ACDC_POWER_SOURCE,
            DEVICE_NOTIFY_WINDOW_HANDLE);

            hBattCapacity = RegisterPowerSettingNotification(hwnd,
            ref GUID_BATTERY_PERCENTAGE_REMAINING,
            DEVICE_NOTIFY_WINDOW_HANDLE);

            hMonitorOn = RegisterPowerSettingNotification(hwnd,
            ref GUID_MONITOR_POWER_ON,
            DEVICE_NOTIFY_WINDOW_HANDLE);

            hPowerScheme = RegisterPowerSettingNotification(hwnd,
            ref GUID_POWERSCHEME_PERSONALITY,
            DEVICE_NOTIFY_WINDOW_HANDLE);
        }

        internal void UnregisterForPowerNotifications()
        {
            UnregisterPowerSettingNotification(hBattCapacity);
            UnregisterPowerSettingNotification(hMonitorOn);
            UnregisterPowerSettingNotification(hPowerScheme);
            UnregisterPowerSettingNotification(hPowerSrc);
        }

        internal void PowerSettingChange(Message m)
        {
            //known bug with debugger
            try
            {
                POWERBROADCAST_SETTING ps =
             (POWERBROADCAST_SETTING)Marshal.PtrToStructure(
                 m.LParam, typeof(POWERBROADCAST_SETTING));

                IntPtr pData = (IntPtr)((int)m.LParam + Marshal.SizeOf(ps));

                if (ps.PowerSetting == GUID_POWERSCHEME_PERSONALITY)
                {
                    SetPowerPlan(ps, pData);
                }
                else if (ps.DataLength == Marshal.SizeOf(typeof(Int32)))
                {
                    Int32 iData = (Int32)Marshal.PtrToStructure(pData, typeof(Int32));
                    if (ps.PowerSetting == GUID_BATTERY_PERCENTAGE_REMAINING)
                    {
                        SetBatteryLevel(iData);
                    }
                    else if (ps.PowerSetting == GUID_MONITOR_POWER_ON)
                    {
                        SetMonitorState(iData);
                    }
                    else if (ps.PowerSetting == GUID_ACDC_POWER_SOURCE)
                    {
                        SetPowerSource(iData);
                    }
                }
            }
            catch
            {
                if (Helper.IsFusionEnabled)
                    Trace.WriteLine("Error occures (known one?)");
            }
        }

        internal NativeMethods.SYSTEM_BATTERY_STATE GetPowerState()
        {
            IntPtr status = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(NativeMethods.SYSTEM_BATTERY_STATE)));
            uint retval = NativeMethods.CallNtPowerInformation(
              NativeMethods.POWER_INFORMATION_LEVEL.SystemBatteryState,
              (IntPtr)null,
              0,
              status,
              (UInt32)Marshal.SizeOf(typeof(NativeMethods.SYSTEM_BATTERY_STATE))
              );

            return (NativeMethods.SYSTEM_BATTERY_STATE)Marshal.PtrToStructure(status, typeof(NativeMethods.SYSTEM_BATTERY_STATE));

        }

        private void SetPowerSource(int iData)
        {
            if (Helper.IsFusionEnabled)
                Trace.WriteLine("Power source: " + iData);

            //battery - 1
            //AC - 0

            HandleAero(iData == 0);

            HandleSidebar(iData == 0);

            if (iData == 0 && Settings.Default.PWPlug != Guid.Empty)
            {
                Helper.SetActivePower(Settings.Default.PWPlug);
            }
            if (iData == 1 && Settings.Default.PWBattery != Guid.Empty)
            {
                Helper.SetActivePower(Settings.Default.PWBattery);
            }
            

            UpdateVisualStatus();
        }

        private void SetMonitorState(int iData)
        {
            if (Helper.IsFusionEnabled)
                Trace.WriteLine("Monitor State: " + iData);
            if (iData == 0 && Settings.Default.PWIdle != Guid.Empty)
            {
                Helper.SetActivePower(Settings.Default.PWIdle);
            }
        }

        private void SetBatteryLevel(int iData)
        {
            if (Helper.IsFusionEnabled)
                Trace.WriteLine("Battery level: " + iData);

            //Level aware Aero
            if (Settings.Default.AeroActive == 2)
            {
                HandleAero(Settings.Default.AeroActiveLevel < iData);
            }

            //Level aware Sidebar
            if (Settings.Default.SidebarActive == 2)
            {
                HandleSidebar(Settings.Default.SidebarActiveLevel < iData);
            }

            UpdateVisualStatus();
        }

        private void SetPowerPlan(POWERBROADCAST_SETTING ps, IntPtr pData)
        {
            string eventText = string.Empty;
            if (ps.DataLength == Marshal.SizeOf(typeof(Guid)))
            {
                Guid newPersonality =
                    (Guid)Marshal.PtrToStructure(pData, typeof(Guid));
                if (newPersonality == GUID_MAX_POWER_SAVINGS)
                {
                    eventText = "Switched to Max Power savings";
                }
                else if (newPersonality == GUID_MIN_POWER_SAVINGS)
                {
                    eventText = "Switched to Min Power savings";
                }
                else if (newPersonality == GUID_TYPICAL_POWER_SAVINGS)
                {
                    eventText = "Switched to Automatic Power savings";
                }
                else
                {
                    eventText = "switched to unknown Power savings";
                }
                
            }
            if(Helper.IsFusionEnabled)
                Trace.WriteLine(eventText);

        }

        internal void HandleAero(bool enable)
        {
            if (Helper.IsVistaAeroSupport)
            {
                if (Settings.Default.AeroActive == 1)
                {
                    NativeMethods.DwmEnableComposition(true);
                }
                else
                {
                    NativeMethods.DwmEnableComposition(enable);
                }
            }            
        }

        internal void HandleSidebar(bool enable)
        {
            if (IsSidebarActiveOnStartup)
            {
                if (Settings.Default.SidebarActive == 1)
                {
                    Helper.EnableSidebar(true);
                }
                else
                {
                    Helper.EnableSidebar(enable);
                }
            }
        }

        void UpdateVisualStatus()
        {
            frmMain frm = VistaBatterySaver.Program.app.AppForm as frmMain;
            if (frm != null)
            {
                frm.SetStatus(Helper.IsAeroRunning, Helper.IsSidebarRunning);
            }
        }

        public static bool IsSidebarActiveOnStartup = false;

        private PowerMngr() { }

        static PowerMngr mgr;
        public static PowerMngr GetManager()
        {
            if (mgr == null)
                mgr = new PowerMngr();

            return mgr;
        }

        

    }
}
